#include <stdio.h>
#include <string.h>
#include <libxml/parser.h>
#include <libxml/tree.h>

#define MAX_PATH_LENGTH	1024
#define MAX_NODE_DEPTH	16
#define MAX_NODE_LENGTH	64

#if 1
typedef struct notify_node_t{
	char *full_path;
	int notify;
	char *old_val;
	char *new_val;
	struct notify_node_t *next;
	struct notify_node_t *prev;
}NOTIFY_NODE_T;

typedef struct notify_list_t{
	int len;
	NOTIFY_NODE_T *head;
}NOTIFY_LIST_T;

NOTIFY_LIST_T ntfl = {0, NULL, NULL};


NOTIFY_NODE_T * notify_node_malloc()
{
	NOTIFY_NODE_T *node = NULL;
	
	node = (NOTIFY_NODE_T *)malloc(sizeof(NOTIFY_NODE_T));

	return node;
}

void notify_node_init(NOTIFY_NODE_T *node)
{
	//printf("----- %s -----\n", __FUNCTION__);
	if(!node)
	{
		printf("%s node is empty\n", __FUNCTION__);
		return;
	}
	node->full_path = NULL;
	node->notify = 0;
	node->old_val = NULL;
	node->new_val = NULL;
	node->next = NULL;
	node->prev = NULL;
}

void notify_node_print(NOTIFY_NODE_T *node)
{
	//printf("----- %s -----\n", __FUNCTION__);
	printf("\tfull_path = [%s], notify = [%d]\n", node->full_path, node->notify);
	printf("\told_val = [%s], new_val = [%s]\n", 
		node->old_val==NULL?"":node->old_val, node->new_val==NULL?"":node->new_val);
	//printf("new_val = [%s]\n", node->new_val==NULL?"":node->new_val);
}

void notify_node_free(NOTIFY_NODE_T *node)
{
	//printf("----- %s -----\n", __FUNCTION__);
	if(node)
	{
		if(node->full_path)
			free(node->full_path);
		if(node->old_val)
			free(node->old_val);
		if(node->new_val)
			free(node->new_val);
		free(node);
	}
}

void notify_node_set(NOTIFY_NODE_T *n, char *value)
{
	if(n->old_val)
		free(n->old_val);
	n->old_val = n->new_val;
	n->new_val = strdup(value);
}

int notify_node_changed(NOTIFY_NODE_T *node)
{
	int ret = 0;

	if(!node)
		return ret;

	if(!node->old_val || !node->new_val)
		return ret;

	if(strcmp(node->old_val, node->new_val))
		ret = 1;

	return ret;
}

void notify_list_init(NOTIFY_LIST_T *list)
{
	//printf("----- %s -----\n", __FUNCTION__);
	list->len = 0;
	list->head = notify_node_malloc();
	notify_node_init(list->head);
	list->head->next = list->head;
	list->head->prev = list->head;
}

void notify_list_add(NOTIFY_LIST_T *list, NOTIFY_NODE_T *node)
{
	NOTIFY_NODE_T *head = list->head;	

	//printf("----- %s -----\n", __FUNCTION__);
	if(node){
		head->prev->next = node;
		node->next = head;
		node->prev = head->prev;
		head->prev = node;
		list->len++;
	}
}

void notify_list_del(NOTIFY_LIST_T *list, NOTIFY_NODE_T *node)
{
	NOTIFY_NODE_T *n;
	NOTIFY_NODE_T *prev;

	//printf("----- %s -----\n", __FUNCTION__);
	for(n = list->head->next; n != list->head; n = n->next)
	{
		if(!strcmp(n->full_path, node->full_path))
		{
			prev = n->prev;
			prev->next = n->next;
			n->next->prev = prev;
			notify_node_free(node);
			list->len--;
		}
	}
}

void notify_list_add_by_para(NOTIFY_LIST_T *list, char *path, int notify)
{
	NOTIFY_NODE_T *node;

	//printf("----- %s -----\n", __FUNCTION__);
	node = notify_node_malloc();
	notify_node_init(node);
	node->full_path = strdup(path);
	node->notify = notify;
	notify_list_add(list, node);
}

void notify_list_del_by_para(NOTIFY_LIST_T *list, char *full_path)
{
	NOTIFY_NODE_T *n;
	NOTIFY_NODE_T *prev;

	//printf("----- %s -----\n", __FUNCTION__);
	//printf("--- del full_path =[%s]\n", full_path);
	for(n = list->head->next; n != list->head; n = n->next)
	{
		if(!strcmp(n->full_path, full_path))
		{
			prev = n->prev;
			prev->next = n->next;
			n->next->prev = prev;
			notify_node_free(n);
			list->len--;
		}
	}
}

void notify_list_set(NOTIFY_LIST_T *list, char *full_path, char *value)
{
	NOTIFY_NODE_T *n;

	//printf("----- %s -----\n", __FUNCTION__);
	for(n = list->head->next; n != list->head; n = n->next)
	{
		if(!strcmp(n->full_path, full_path))
		{
			notify_node_set(n, value);
		}
	}
}

void notify_list_set_all(NOTIFY_LIST_T *list, char *value)
{
	NOTIFY_NODE_T *n;

	printf("%s set all value to [%s]\n", __FUNCTION__, value);
	for(n = list->head->next; n != list->head; n = n->next)
	{
		notify_node_set(n, value);
	}
}

void notify_list_del_test(NOTIFY_LIST_T *list)
{
	NOTIFY_NODE_T *node;
	int idx = 0;
	char del_list[5][256] = {
		"InternetGatewayDevice.LANDevice.2.WLANConfiguration.2.Enable",
		"InternetGatewayDevice.LANDevice.2.WLANConfiguration.2.SSID",
		"InternetGatewayDevice.LANDevice.2.WLANConfiguration.2.TransmitPower",
		"InternetGatewayDevice.LANDevice.2.WLANConfiguration.2.PreSharedKey.1.KeyPassphrase",
		""
	};

	for(idx = 0; idx < 5; idx++)
	{
		notify_list_del_by_para(list, del_list[idx]);
	}
}

void notify_list_set_test(NOTIFY_LIST_T *list)
{
	NOTIFY_NODE_T *node;
	int idx = 0;
	char set_list[5][256] = {
		"InternetGatewayDevice.LANDevice.2.WLANConfiguration.3.Enable",
		"InternetGatewayDevice.LANDevice.2.WLANConfiguration.3.SSID",
		"InternetGatewayDevice.LANDevice.2.WLANConfiguration.3.TransmitPower",
		"InternetGatewayDevice.LANDevice.2.WLANConfiguration.3.PreSharedKey.1.KeyPassphrase",
		""
	};

	for(idx = 0; idx < 5; idx++)
	{
		notify_list_set(list, set_list[idx], "new value");
	}
}

void notify_list_check_change(NOTIFY_LIST_T *list)
{
	NOTIFY_NODE_T *node;

	printf("---------- %s ----------\n", __FUNCTION__);
	for(node = list->head->next; node != list->head; node = node->next)
	{
		if(notify_node_changed(node)){
			// notify this node
			printf("=== below node_changed!!!\n");
			notify_node_print(node);
		}
	}
}

void notify_list_destroy(NOTIFY_LIST_T *list)
{
	NOTIFY_NODE_T *node;
	NOTIFY_NODE_T *next;

	//printf("----- list len = [%d] -----\n", list->len);
	for(node = list->head->next; node != list->head; node = next)
	{
		next = node->next;
		notify_node_free(node);
	}
	notify_node_free(list->head);
}

void notify_list_print(NOTIFY_LIST_T *list)
{
	NOTIFY_NODE_T *node;
	int idx = 0;

	printf("----- list print -----\n");
	printf("----- list len = [%d] -----\n", list->len);
	for(node = list->head->next; node != list->head; node = node->next)
	{
		idx++;
		printf("\t--- idx=[%4d] ---\n", idx);
		notify_node_print(node);
	}
}

#endif

FILE *fp1 = 0;

char full_path[MAX_PATH_LENGTH];
char node_path[MAX_NODE_DEPTH][MAX_NODE_LENGTH];
int  node_depth = 0;
int  in_node = 0;


void print_element_names(xmlNode * a_node)
{
    xmlNode *cur_node = NULL;
	xmlChar *attr_notify = NULL;
	char *node_name;
	char tmp[16];
	int i = 0;
	int notify = 0;

	for (cur_node = a_node; cur_node; cur_node = cur_node->next) 
	{
		if (cur_node->type == XML_ELEMENT_NODE) 
		{
			node_name = (char *)cur_node->name;
			attr_notify = xmlGetProp(cur_node, "Notification");
			if(!node_name)
				continue;
			if(node_name[0] == 'i' && atoi(node_name+1) > 0)
				node_name++;

			if (!attr_notify) 
			{
				strcpy(node_path[node_depth], node_name);
				node_depth++;
			}		
			else 
			{
				in_node = 1;
				memset(full_path, 0, sizeof(full_path));
				for (i=0 ; i<node_depth ; i++){
					strcat(full_path, node_path[i]);
					strcat(full_path, ".");
				}
				strcat(full_path, node_name);
				notify = attr_notify==NULL?0:atoi(attr_notify);
				if(notify > 0){ // only add the parameter need notify to monitor list
					fprintf(fp1, "path=\"%s\", notify=[%d]\n", full_path, notify);
					notify_list_add_by_para(&ntfl, full_path, notify);
				}
			}
			node_depth++;
			if (node_depth >= MAX_NODE_DEPTH) 
			{
				printf("Error : Too many layers in Data Model, try increase attribute depth ...! \n");
				exit(1);
			}
		}
		else if (cur_node->type == XML_TEXT_NODE)
		{
			if (*(cur_node->content) < ' ') 
			{	// </node>
				if (node_depth) 
					node_depth--;
				if (in_node) {	
					in_node = 0;	
				}
			}
			else 
			{
				if (*(cur_node->content) != '\0') 
				{
					in_node = 0;	
				} 
			}
		}
		print_element_names(cur_node->children);
    }
}


int main(int argc, char **argv)
{
    xmlDoc *doc = NULL;
    xmlNode *root_element = NULL;
    int i;

    if (argc != 2)
        return(1);

    LIBXML_TEST_VERSION

    doc = xmlReadFile(argv[1], NULL, 0);
    if (doc == NULL) {
        printf("Error: can not parse file %s\n", argv[1]);
        return(1);
    }

	fp1 = fopen("node_path.h", "w");
	if (!fp1) {
        printf("Error: can not create file %s\n", "tr69_table.h");
        return(1);
    }
   
	printf("list init start\n");
	notify_list_init(&ntfl); 
	printf("list init finished, start add list\n");
    root_element = xmlDocGetRootElement(doc);
    print_element_names(root_element);
	notify_list_print(&ntfl); 
	notify_list_del_test(&ntfl);
	notify_list_print(&ntfl); 
	notify_list_set_all(&ntfl, "");
	notify_list_check_change(&ntfl);
	notify_list_set_all(&ntfl, "");
	notify_list_check_change(&ntfl);
	notify_list_set_test(&ntfl);
	notify_list_check_change(&ntfl);
	notify_list_print(&ntfl); 
	notify_list_destroy(&ntfl);
    fclose(fp1);

	xmlFreeDoc(doc);
	xmlCleanupParser();

	return 0;
}
